package com.googlecode.restitory.gae.hook;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import com.google.appengine.api.datastore.DatastoreService;
import com.google.appengine.api.datastore.DatastoreServiceFactory;
import com.google.appengine.api.datastore.Entity;
import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.PreparedQuery;
import com.google.appengine.api.datastore.Query;
import com.google.appengine.api.datastore.Query.FilterOperator;
import com.google.appengine.api.datastore.Query.SortDirection;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
import com.googlecode.mycontainer.commons.json.JsonHandler;
import com.googlecode.restitory.gae.Content;
import com.googlecode.restitory.gae.Direction;
import com.googlecode.restitory.gae.GAERequestUtil;
import com.googlecode.restitory.gae.Operator;
import com.googlecode.restitory.gae.Request;
import com.googlecode.restitory.gae.RequestJsoner;
import com.googlecode.restitory.gae.Response;

public class QueryHook implements Hook {

	@Override
	public Response execute(Request request, Chain chain) {
		String path = request.getPath();
		if (path.equals("/_/query/content/text")) {
			return queryContentText(request);
		}
		if (path.equals("/_/query/content")) {
			return queryContent(request);
		}
		if (path.equals("/_/query")) {
			return query(request);
		}
		return chain.execute(request);
	}

	public DatastoreService getDatastoreService() {
		return DatastoreServiceFactory.getDatastoreService();
	}

	private Response queryContentText(Request req) {
		DatastoreService ds = getDatastoreService();
		List<Entity> entities = queryKeys(req);
		List<Key> keys = new ArrayList<Key>();
		for (Entity entity : entities) {
			Key key = entity.getKey();
			keys.add(GAERequestUtil.toContentKey(key));
		}
		Map<Key, Entity> map = ds.get(keys);
		JsonArray array = new JsonArray();
		for (Key key : keys) {
			Entity entity = map.get(key);
			Content content = GAERequestUtil.getContent(entity);
			JsonObject contentObject = RequestJsoner.formatJsonElement(content);
			JsonObject entry = new JsonObject();
			entry.addProperty("path", key.getName());
			entry.add("content", contentObject.getAsJsonPrimitive("value"));
			array.add(entry);
		}
		String json = RequestJsoner.format(array);
		return new Response(req).setCode(200).setContentType("application/json").setContent(json);
	}

	private Response queryContent(Request req) {
		DatastoreService ds = getDatastoreService();
		List<Entity> entities = queryKeys(req);
		List<Key> keys = new ArrayList<Key>();
		for (Entity entity : entities) {
			Key key = entity.getKey();
			keys.add(GAERequestUtil.toContentKey(key));
		}
		Map<Key, Entity> map = ds.get(keys);
		JsonArray array = new JsonArray();
		for (Key key : keys) {
			Entity entity = map.get(key);
			Content content = GAERequestUtil.getContent(entity);
			JsonObject contentObject = RequestJsoner.formatJsonElement(content);
			JsonObject entry = new JsonObject();
			entry.addProperty("path", key.getName());
			entry.add("content", contentObject);
			array.add(entry);
		}
		String json = RequestJsoner.format(array);
		return new Response(req).setCode(200).setContentType("application/json").setContent(json);
	}

	private Response query(Request req) {
		List<Entity> list = queryKeys(req);
		List<String> ret = new ArrayList<String>(list.size());
		for (Entity entity : list) {
			ret.add(entity.getKey().getName());
		}
		String json = JsonHandler.instance().format(ret);
		return new Response(req).setCode(200).setContentType("application/json").setContent(json);
	}

	private List<Entity> queryKeys(Request req) {
		Query query = new Query("resource");
		query.setKeysOnly();
		List<String> filters = req.getParams().get("f");
		for (String filter : filters) {
			addFilter(query, filter);
		}
		List<String> orders = req.getParams().get("s");
		for (String order : orders) {
			addOrder(query, order);
		}
		Integer offset = req.getParams().getAsInteger("o");
		Integer limit = req.getParams().getAsInteger("l");
		PreparedQuery prepared = getDatastoreService().prepare(query);
		List<Entity> list = prepared.asList(GAERequestUtil.getFetch(offset, limit));
		return list;
	}

	private void addOrder(Query query, String order) {
		String[] split = order.split(":");
		Direction dir = Direction.valueOf(split[1]);
		SortDirection sd = GAERequestUtil.directionToSort(dir);
		query.addSort(split[0], sd);
	}

	private void addFilter(Query query, String filter) {
		String[] split = filter.split(":");
		Operator op = Operator.valueOf(split[1]);
		FilterOperator filterOperator = GAERequestUtil.operatorToFilter(op);

		JsonParser parser = new JsonParser();
		JsonElement value = parser.parse(split[2]);
		Object v = GAERequestUtil.getJsonValue(value);
		query.addFilter(split[0], filterOperator, v);
	}

}
